
#include "nane/testframework/testframework.h"

#include <cstdlib>

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static void s_loadArchiveSystemModule( const ModulePtr& _module )
    {
        FileArchiveSystem* fileArchiveSystem = new nane::FileArchiveSystem();
        if( fileArchiveSystem->Initialize(_module) == true )
        {
            FileArchiveManager::AddInterface(fileArchiveSystem->GetFileType(), fileArchiveSystem);
        }
        else
        {
            delete fileArchiveSystem;
        }
    }    
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static void s_loadImageCodecSystemModule( const ModulePtr& _module )
    {
        ImageCodecSystem* imageCodecSystem = new nane::ImageCodecSystem();
        if( imageCodecSystem->Initialize(_module) == true )
        {
            ImageCodecManager::AddInterface(imageCodecSystem->GetFileType(), imageCodecSystem);
        }
        else
        {
            delete imageCodecSystem;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static void s_loadSoundCodecSystemModule( const ModulePtr& _module )
    {
        SoundCodecSystem* soundCodecSystem = new nane::SoundCodecSystem();
        if( soundCodecSystem->Initialize(_module) == true )
        {
            SoundCodecManager::AddInterface(soundCodecSystem->GetFileType(), soundCodecSystem);
        }
        else
        {
            delete soundCodecSystem;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    TestFramework::TestFramework()
        : m_windowTitle( "Not Set" )
        , m_windowWidth( 800 )
        , m_windowHeight( 600 )
        , m_running( true )
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    TestFramework::~TestFramework()
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    int TestFramework::Run( const char* _commandLine )
    {
        if( Initialize("") == false )
            return EXIT_FAILURE;

        OnSetup();
        m_frameTime = Time::GetMilliseconds();
        while( m_running )
        {
            Update();
        }
        OnShutdown();
        Shutdown();
        return EXIT_SUCCESS;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::Update()
    {
        uint32 time = Time::GetMilliseconds();
        uint32 dt = time - m_frameTime;
        m_frameTime = time;
        m_inputSystem.Update();
        m_window.UpdateMessages();
        m_soundSystem.Update(dt);
        OnUpdate(dt);
        OnRender();
        m_renderSystem.SwapBuffers();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool TestFramework::Initialize( const char* _assetsDirectory, ILogger* _platformLogger /*= NULL*/ )
    {
        if( nane::Initialize() == false )
        {
            return false;
        }
        LogManager::SetMessagesLevel(ELogMessageLevel::Debug);
        LogManager::RegisterLogger(_platformLogger);
        StackString<32> logfilename("log_%1.txt");
        logfilename << Time::GetLocalDate();
        if( m_fileLogger.Open(logfilename.c_str(), true) == true )
        {
            LogManager::RegisterLogger(&m_fileLogger);
        }
        LOG_INFO("Session started");
       
        m_serializeSystem.Initialize();
        ModuleManager::ForeachModule("IFileArchiveSystem", s_loadArchiveSystemModule);
        FileArchiveManager::AddAlias("apk", "zip");  //  for android
        m_assets = FileArchiveManager::Instance(_assetsDirectory)->CreateArchive(_assetsDirectory);
        if( m_assets == NullPtr )
        {
            LOG_ERROR("Failed to mount assets at path '%1'") << _assetsDirectory;
            return false;
        }
        ReadConfig("assets/TestFramework.ini");
        m_windowSystem.Initialize();
        if( m_renderSystem.Initialize() == false )
            return false;

        uint32 screenWidth = m_windowSystem.GetScreenWidth();
        uint32 screenHeight = m_windowSystem.GetScreenHeight();

        uint32 posX = (screenWidth - m_windowWidth) / 2;
        uint32 posY = (screenHeight - m_windowHeight) / 2;

        m_window = m_windowSystem.CreateWindow(m_windowTitle.c_str(), m_windowWidth, m_windowHeight, posX, posY);
        m_window.SetMessageHandler(this);
        m_renderSystem.InitializeContext(m_window.GetOSHandle(), m_windowWidth, m_windowHeight);

        m_window.Show();

        m_soundSystem.Initialize();
        m_inputSystem.Initialize(m_window.GetOSHandle());
        
        m_scriptSystem.Initialize();
        m_scriptSystem.ExportGlobalFunction("InputSystem", "SubscribeActionKey", MakeMemberFunctor(&TestFramework::ScriptSubscribeActionKey, this));
        m_scriptSystem.ExportGlobalFunction("InputSystem", "GetActionValue", MakeMemberFunctor(&InputSystem::GetActionValue, &m_inputSystem));
        m_scriptSystem.ExportGlobalFunction("InputSystem", "GetActionWasPressed", MakeMemberFunctor(&InputSystem::GetActionWasPressed, &m_inputSystem));
        m_scriptSystem.ExportGlobalFunction("InputSystem", "GetActionIsPressed", MakeMemberFunctor(&InputSystem::GetActionIsPressed, &m_inputSystem));
        m_scriptSystem.ExportGlobalFunction("InputSystem", "GetActionWasReleased", MakeMemberFunctor(&InputSystem::GetActionWasReleased, &m_inputSystem));
        m_scriptSystem.ExportGlobalFunction("InputSystem", "GetActionIsReleased", MakeMemberFunctor(&InputSystem::GetActionIsReleased, &m_inputSystem));
        //ScriptSystem::exportFunction(inputModule, "InputSystem", "SubscribeAxis", &InputSystem::subscribeAxis<ScriptFunctor>);
        
        ModuleManager::ForeachModule("IImageCodecSystem", s_loadImageCodecSystemModule);
        ModuleManager::ForeachModule("ISoundCodecSystem", s_loadSoundCodecSystemModule);

        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::Shutdown()
    {
        SoundCodecManager::DestroyAll();
        ImageCodecManager::DestroyAll();
        m_scriptSystem.Shutdown();
        m_inputSystem.Shutdown();
        m_inputSystem.Shutdown();
        m_soundSystem.Shutdown();
        m_window = NullPtr;
        m_renderSystem.Shutdown();
        m_assets = NullPtr;
        FileArchiveManager::DestroyAll();
        m_serializeSystem.Shutdown();
        m_windowSystem.Shutdown();
        LOG_INFO("Session successfully ended");
        LogManager::UnregisterLogger(&m_fileLogger);
        m_fileLogger.Close();
        nane::Shutdown();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::OnClose()
    {
        m_running = false;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::ReadConfig(const char* _iniFilePath)
    {
        ReadFilePtr file = m_assets.OpenFile(_iniFilePath);

        if( file != NullPtr )
        {
            DeserializationProtocol<> protocol;
            protocol.Deserialize("WindowWidth", &m_windowWidth);
            protocol.Deserialize("WindowHeight", &m_windowHeight);
            protocol.Deserialize("WindowTitle", &m_windowTitle);
            protocol.DeserializeFunction("Module", &ModuleManager::LoadModule);

            uint32 bufferSize = file->Size();
            byte* buffer = new byte[bufferSize];

            m_serializeSystem.Deserialize(file, &protocol, buffer, bufferSize);

            delete[] buffer;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::ScriptSubscribeActionKey( const char* _action, int _key, float _scale )
    {
        m_inputSystem.AddActionBinding(_action, InputBinding(enum_cast<EKeyCode>(static_cast<byte>(_key)), _scale));
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::OnSetup()
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::OnShutdown()
    {
        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::OnUpdate( uint32 _dt )
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::OnRender()
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
